"""
Factory functions for creating instances of registered components.

This module provides a unified interface for creating and configuring
different types of components from the registry system.
"""

from typing import Any, Dict, Set, TypeVar, Union, Type
import logging

# Import interfaces for better type annotations
from learners.interfaces import (
    LearnerInterface,
    MemoryInterface,
    PrototypeInterface,
    ClassifierAlignmentInterface,
    DriftCompensationInterface,
    TrainingInterface,
    EvaluationInterface,
)
from .registry import (
    STANDARD_REGISTRIES,
    LEARNER_REGISTRY,
    TRAINING_STRATEGY_REGISTRY,
    DRIFT_COMPENSATION_REGISTRY,
    PROTOTYPE_MANAGER_REGISTRY,
    MEMORY_MANAGER_REGISTRY,
    CLASSIFIER_ALIGNMENT_REGISTRY,
    EVALUATION_MANAGER_REGISTRY,
)

T = TypeVar("T")
I = TypeVar("I")  # Interface type

# Type variables for concrete implementations
L = TypeVar("L", bound=LearnerInterface)
TS = TypeVar("TS", bound=TrainingInterface)
CA = TypeVar("CA", bound=ClassifierAlignmentInterface)
PM = TypeVar("PM", bound=PrototypeInterface)


def create_component(registry_name: str, component_name: str, **kwargs) -> Union[
    LearnerInterface,
    TrainingInterface,
    DriftCompensationInterface,
    PrototypeInterface,
    MemoryInterface,
    ClassifierAlignmentInterface,
    EvaluationInterface
]:
    """Generic function to create any registered component with preserved type."""
    if registry_name not in STANDARD_REGISTRIES:
        available = sorted(STANDARD_REGISTRIES.keys())
        raise ValueError(
            f"Unknown registry '{registry_name}'. Available registries: {available}"
        )

    registry = STANDARD_REGISTRIES[registry_name]
    try:
        # Return will be appropriately typed based on which registry is used
        return registry.create(component_name, **kwargs)
    except Exception as e:
        logging.error(
            f"Error creating {registry_name} component '{component_name}': {e}"
        )
        raise


# Specialized factory functions for common component types


def create_learner(
    learner_type: str,
    args: Dict[str, Any],
    data_manager: Any,
    model_func: Any,
    **kwargs,
) -> LearnerInterface:
    """Create a learner instance from the registry.

    Args:
        learner_type: Type of learner to create
        args: Configuration arguments
        data_manager: Data manager instance
        model_func: Model factory function
        **kwargs: Additional arguments

    Returns:
        Configured learner instance implementing LearnerInterface
    """
    try:
        # Using create directly to ensure type-checking validates the result
        return LEARNER_REGISTRY.create(
            learner_type,
            args=args,
            data_manager=data_manager,
            model_func=model_func,
            **kwargs,
        )
    except Exception as e:
        logging.error(f"Failed to create learner '{learner_type}': {e}")
        raise


def create_training_strategy(strategy_type: str, **kwargs) -> TrainingInterface:
    """Create a training strategy instance from the registry.

    Args:
        strategy_type: Name of the training strategy to create
        **kwargs: Arguments to pass to the strategy constructor

    Returns:
        Training strategy implementation with concrete type preserved
    """
    # The Registry<TrainingInterface>.create() returns the concrete type
    # that implements TrainingInterface
    return TRAINING_STRATEGY_REGISTRY.create(strategy_type, **kwargs)


def create_training_strategy_typed(strategy_type: str, expected_type: Type[TS], **kwargs) -> TS:
    """Create a training strategy instance with preserved concrete type.

    Args:
        strategy_type: Name of the training strategy to create
        expected_type: Expected concrete implementation type
        **kwargs: Arguments to pass to the strategy constructor

    Returns:
        Training strategy of the specified concrete type

    Raises:
        TypeError: If strategy is not of expected type
    """
    return TRAINING_STRATEGY_REGISTRY.create_typed(strategy_type, expected_type, **kwargs)


def create_evaluation_manager(evaluation_type: str, **kwargs) -> EvaluationInterface:
    """Create an evaluation instance from the registry."""
    return EVALUATION_MANAGER_REGISTRY.create(evaluation_type, **kwargs)


def create_classifier_alignment(
    alignment_type: str, **kwargs
) -> ClassifierAlignmentInterface:
    """Create a classifier alignment instance from the registry."""
    return CLASSIFIER_ALIGNMENT_REGISTRY.create(alignment_type, **kwargs)


def create_classifier_alignment_typed(
    alignment_type: str, expected_type: Type[CA], **kwargs
) -> CA:
    """Create a classifier alignment instance with preserved concrete type."""
    return CLASSIFIER_ALIGNMENT_REGISTRY.create_typed(alignment_type, expected_type, **kwargs)


def create_prototype_manager(manager_type: str, **kwargs) -> PrototypeInterface:
    """Create a prototype manager instance from the registry."""
    return PROTOTYPE_MANAGER_REGISTRY.create(manager_type, **kwargs)


def create_prototype_manager_typed(manager_type: str, expected_type: Type[PM], **kwargs) -> PM:
    """Create a prototype manager instance with preserved concrete type."""
    return PROTOTYPE_MANAGER_REGISTRY.create_typed(manager_type, expected_type, **kwargs)


def create_drift_compensation(
    compensation_type: str, **kwargs
) -> DriftCompensationInterface:
    """Create a drift compensation instance from the registry."""
    return DRIFT_COMPENSATION_REGISTRY.create(compensation_type, **kwargs)


def create_memory_manager(manager_type: str, **kwargs) -> MemoryInterface:
    """Create a memory manager instance from the registry."""
    return MEMORY_MANAGER_REGISTRY.create(manager_type, **kwargs)


def list_available(registry_name: str) -> Set[str]:
    """List available components in a specific registry.

    Args:
        registry_name: Name of the registry to query

    Returns:
        Set of registered component names

    Raises:
        ValueError: If registry name is invalid
    """
    if registry_name not in STANDARD_REGISTRIES:
        available = sorted(STANDARD_REGISTRIES.keys())
        raise ValueError(
            f"Unknown registry '{registry_name}'. " f"Available registries: {available}"
        )

    return STANDARD_REGISTRIES[registry_name].list_registered()


# Convenience functions for listing components by type
def list_learners() -> Set[str]:
    """List all registered learner types."""
    return LEARNER_REGISTRY.list_registered()


def list_training_strategies() -> Set[str]:
    """List all registered training strategy types."""
    return TRAINING_STRATEGY_REGISTRY.list_registered()


def list_classifier_alignments() -> Set[str]:
    """List all registered classifier alignment types."""
    return CLASSIFIER_ALIGNMENT_REGISTRY.list_registered()


def list_prototype_managers() -> Set[str]:
    """List all registered prototype manager types."""
    return PROTOTYPE_MANAGER_REGISTRY.list_registered()


def list_drift_compensations() -> Set[str]:
    """List all registered drift compensation types."""
    return DRIFT_COMPENSATION_REGISTRY.list_registered()


def list_memory_managers() -> Set[str]:
    """List all registered memory manager types."""
    return MEMORY_MANAGER_REGISTRY.list_registered()


def list_evaluations() -> Set[str]:
    """List all registered evaluation types."""
    return EVALUATION_MANAGER_REGISTRY.list_registered()
